package cn.lichenfei.jfx.music;

import cn.hutool.core.io.FastByteArrayOutputStream;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.RuntimeUtil;
import cn.hutool.core.util.StrUtil;

import java.io.File;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

/**
 * @author ChenFei
 * @date 2022年5月25日
 * <p>
 * jpackage 测试工具
 */
public class JPackage {

    public static void main(String[] args) {
        try {
            /**
             * 注意：先运行 mvv package , 将项目生成的jar包放在lib下方便操作
             */
            /************************************************ 请设置基本属性 ************************************************/

            String cmd_path = "C:/jpackage_demo/";//在这个目录下执行命令，会将打包文件放在这个目录下。
            FileUtil.mkdir(cmd_path);
            String[] envp = {"D:/my_application/openjdk17/jdk-17.0.2/bin"};//jdk环境变量，如果配置了可以去掉。
            //
            String input = "D:/idea_project/git/cf-jfx-music/target/lib";//所有的jar文件所在目录
            String name = "音乐播放器";//生成的包名称
            String mainClass = "cn.lichenfei.jfx.music.StartApp";//启动类
            String mainJar = "cf-jfx-music-2022.05.jar";//启动jar包
            String icon = "D:/idea_project/git/cf-jfx-music/src/main/resources/cn/lichenfei/jfx/music/img/icon.ico";//icon路径

            /************************************************ 请设置基本属性 ************************************************/

            //1:直接打包
            //jpackage(envp, cmd_path, input, name, mainClass, mainJar, icon, null);

            //2：jdeps:分析项目所依赖模块进行打包：体积比上边的小了很多。
            Set<String> modules = jdeps(envp, input);
            //根据项目所依赖的模块制作jre镜像
            String jlink_jre = cmd_path + "jlink-jre";
            jlink(envp, cmd_path, modules, jlink_jre);
            //jpackage打包
            jpackage(envp, cmd_path, input, name, mainClass, mainJar, icon, jlink_jre);

            //jar文件检测，补偿
            String appFile = cmd_path + name + "/app";
            List<String> strings = FileUtil.listFileNames(appFile);
            for (int i = 0; i < strings.size(); i++) {
                if (StrUtil.endWith(strings.get(i), ".jar")){
                    return;
                }
            }
            Console.log("jar文件未生成，开始复制。。。");
            FileUtil.copyContent(new File(input), new File(appFile), false);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 使用jlink制作java运行环境（jre）
     *
     * @param envp
     * @param cmd_path
     * @param output
     * @throws InterruptedException
     */
    private static void jlink(String[] envp, String cmd_path, Set<String> modules, String output) throws InterruptedException {
        //初始化命令
        StringJoiner sj = new StringJoiner(" ");
        sj.add("jlink --add-modules");
        String modulesString = modules.stream().collect(Collectors.joining(","));
        sj.add(modulesString);
        sj.add("--compress=2");//0：无压缩,1：常量字符串共享2：压缩
        sj.add("--output");//输入路径
        sj.add(output);
        //
        String cmd = sj.toString();
        Console.log("-------------------------------------------------------------");
        Console.log("jlink命令：{}", cmd);
        Console.log("正在制作jdk镜像。。。");
        Process process = RuntimeUtil.exec(envp, new File(cmd_path), cmd);
        FastByteArrayOutputStream read = IoUtil.read(process.getInputStream());
        process.waitFor();
        String s = read.toString("gbk");
        Console.log("-------------------------------------------------------------");
        Console.log("jlink命令结果：{}", StrUtil.isBlank(s) ? "成功" : s);
    }

    /**
     * 使用jdeps获取项目所有jar所依赖的jdk模块
     *
     * @param envp
     * @param input
     * @throws InterruptedException
     */
    private static Set<String> jdeps(String[] envp, String input) throws InterruptedException {
        //初始化命令
        StringJoiner sj = new StringJoiner(" ");
        sj.add("jdeps");
        //sj.add("-cp");//路径
        //sj.add(cmd_path);
        sj.add("--multi-release 17");//版本
        sj.add("--print-module-deps");//逗号分隔打印
        sj.add("--ignore-missing-deps");//忽略缺少被依赖的对象
        String sjString = sj.toString();
        //获取当前路径下的所有jar文件
        List<String> jarList = FileUtil.listFileNames(input);
        int size = jarList.size();
        Set<String> modules = new HashSet<>();
        for (int i = 0; i < size; i++) {
            String cmd = sjString + " " + jarList.get(i);
            Console.log("-------------------------------------------------------------");
            Console.log("jdeps命令：{}", cmd);
            Process process = RuntimeUtil.exec(envp, new File(input), cmd);
            FastByteArrayOutputStream read = IoUtil.read(process.getInputStream());
            process.waitFor();
            String s = read.toString("gbk");
            if (StrUtil.isNotBlank(s)) {
                s = StrUtil.trim(s);
                List<String> split = StrUtil.split(s, ",");
                modules.addAll(split);
            }
            Console.log("-------------------------------------------------------------");
            Console.log("jdeps结果：{}", s);
        }
        Console.log("-------------------------------------------------------------");
        Console.log("jdeps总结果：{}", modules.toString());
        return modules;
    }

    /**
     * jpackage打包
     *
     * @param envp
     * @param cmd_path
     * @param input
     * @param name
     * @param mainClass
     * @param mainJar
     * @param icon
     * @throws InterruptedException
     */
    private static void jpackage(String[] envp, String cmd_path, String input, String name,
                                 String mainClass, String mainJar, String icon, String runtimeImage) throws InterruptedException {
        //初始化命令
        StringJoiner sj = new StringJoiner(" ");
        sj.add("jpackage --type app-image");
        sj.add("--input");
        sj.add(input);
        sj.add("--name");
        sj.add(name);
        sj.add("--main-class");
        sj.add(mainClass);
        sj.add("--main-jar");
        sj.add(mainJar);
        sj.add("--icon");
        sj.add(icon);
        if (StrUtil.isNotBlank(runtimeImage)) {
            sj.add("--runtime-image");
            sj.add(runtimeImage);
        }
        //
        String cmd = sj.toString();
        Console.log("-------------------------------------------------------------");
        Console.log("jpackage命令：{}", cmd);
        Console.log("正在打包。。。");
        Process process = RuntimeUtil.exec(envp, new File(cmd_path), cmd);
        FastByteArrayOutputStream read = IoUtil.read(process.getInputStream());
        process.waitFor();
        String s = read.toString("gbk");
        Console.log("-------------------------------------------------------------");
        Console.log("jpackage结果：{}", StrUtil.isBlank(s) ? "成功" : s);
    }
}

